﻿/*
**  ContactManager
*/
if (!$.Engine.Dynamic || typeof $.Engine.Dynamic === "undefined") $.Engine.Dynamic = {};
$.Engine.Dynamic.ContactManager = function () {
  this.m_nullContact = $.extend(
    true, new $.Engine.Dynamic.Contact.Contact(), new $.Engine.Dynamic.Contact.NullContact());
  this.m_world = null;
  this.m_destroyImmediate = false;
};
Object.extend($.Engine.Dynamic.ContactManager.prototype, {
  PairAdded: function (proxyUserData1, proxyUserData2) {
    var shape1 = proxyUserData1;
    var shape2 = proxyUserData2;

    var body1 = shape1.m_body;
    var body2 = shape2.m_body;

    if (body1.IsStatic() && body2.IsStatic()) {
      return this.m_nullContact;
    }

    if (shape1.m_body == shape2.m_body) {
      return this.m_nullContact;
    }

    if (body2.IsConnected(body1)) {
      return this.m_nullContact;
    }

    if (this.m_world.m_filter != null && this.m_world.m_filter.ShouldCollide(shape1, shape2) == false) {
      return this.m_nullContact;
    }

    // Ensure that body2 is dynamic (body1 is static or dynamic).
    if (body2.m_invMass == 0.0) {
      var tempShape = shape1;
      shape1 = shape2;
      shape2 = tempShape;
      var tempBody = body1;
      body1 = body2;
      body2 = tempBody;
    }

    // Call the factory.
    var contact = $.Engine.Dynamic.Contact.Contact.Create(shape1, shape2, this.m_world.m_blockAllocator);

    if (contact == null) {
      return this.m_nullContact;
    }
    else {
      // Insert into the world.
      contact.m_prev = null;
      contact.m_next = this.m_world.m_contactList;
      if (this.m_world.m_contactList != null) {
        this.m_world.m_contactList.m_prev = contact;
      }
      this.m_world.m_contactList = contact;
      this.m_world.m_contactCount++;
    }

    return contact;
  },

  // This is a callback from the broadphase when two AABB proxies cease
  // to overlap. We destroy the b2Contact.
  PairRemoved: function (proxyUserData1, proxyUserData2, pairUserData) {
    if (pairUserData == null) {
      return;
    }

    var c = pairUserData;
    if (c != this.m_nullContact) {
      if (this.m_destroyImmediate == true) {
        this.DestroyContact(c);
        c = null;
      }
      else {
        c.m_flags |= $.Engine.Dynamic.Contact.Contact.e_destroyFlag;
      }
    }
  },

  DestroyContact: function (c) {
    // Remove from the world.
    if (c.m_prev) {
      c.m_prev.m_next = c.m_next;
    }

    if (c.m_next) {
      c.m_next.m_prev = c.m_prev;
    }

    if (c == this.m_world.m_contactList) {
      this.m_world.m_contactList = c.m_next;
    }

    // If there are contact points, then disconnect from the island graph.
    if (c.GetManifoldCount() > 0) {
      var body1 = c.m_shape1.m_body;
      var body2 = c.m_shape2.m_body;
      var node1 = c.m_node1;
      var node2 = c.m_node2;

      // Wake up touching bodies.
      body1.WakeUp();
      body2.WakeUp();

      // Remove from body 1
      if (node1.prev) {
        node1.prev.next = node1.next;
      }

      if (node1.next) {
        node1.next.prev = node1.prev;
      }

      if (node1 == body1.m_contactList) {
        body1.m_contactList = node1.next;
      }

      node1.prev = null;
      node1.next = null;

      // Remove from body 2
      if (node2.prev) {
        node2.prev.next = node2.next;
      }

      if (node2.next) {
        node2.next.prev = node2.prev;
      }

      if (node2 == body2.m_contactList) {
        body2.m_contactList = node2.next;
      }

      node2.prev = null;
      node2.next = null;
    }

    // Call the factory.
    $.Engine.Dynamic.Contact.Contact.Destroy(c, this.m_world.m_blockAllocator);
    --this.m_world.m_contactCount;
  },

  // Destroy any contacts marked for deferred destruction.
  CleanContactList: function () {
    var c = this.m_world.m_contactList;
    while (c != null) {
      var c0 = c;
      c = c.m_next;

      if (c0.m_flags & $.Engine.Dynamic.Contact.Contact.e_destroyFlag) {
        this.DestroyContact(c0);
        c0 = null;
      }
    }
  },

  // This is the top level collision call for the time step. Here
  // all the narrow phase collision is processed for the world
  // contact list.
  Collide: function () {
    var body1;
    var body2;
    var node1;
    var node2;

    for (var c = this.m_world.m_contactList; c != null; c = c.m_next) {
      if (c.m_shape1.m_body.IsSleeping() && c.m_shape2.m_body.IsSleeping()) {
        continue;
      }

      var oldCount = c.GetManifoldCount();
      c.Evaluate();

      var newCount = c.GetManifoldCount();

      if (oldCount == 0 && newCount > 0) {
        //b2Settings.b2Assert(c.GetManifolds().pointCount > 0);

        // Connect to island graph.
        body1 = c.m_shape1.m_body;
        body2 = c.m_shape2.m_body;
        node1 = c.m_node1;
        node2 = c.m_node2;

        // Connect to body 1
        node1.contact = c;
        node1.other = body2;

        node1.prev = null;
        node1.next = body1.m_contactList;
        if (node1.next != null) {
          node1.next.prev = c.m_node1;
        }
        body1.m_contactList = c.m_node1;

        // Connect to body 2
        node2.contact = c;
        node2.other = body1;

        node2.prev = null;
        node2.next = body2.m_contactList;
        if (node2.next != null) {
          node2.next.prev = node2;
        }
        body2.m_contactList = node2;
      }
      else if (oldCount > 0 && newCount == 0) {
        // Disconnect from island graph.
        body1 = c.m_shape1.m_body;
        body2 = c.m_shape2.m_body;
        node1 = c.m_node1;
        node2 = c.m_node2;

        // Remove from body 1
        if (node1.prev) {
          node1.prev.next = node1.next;
        }

        if (node1.next) {
          node1.next.prev = node1.prev;
        }

        if (node1 == body1.m_contactList) {
          body1.m_contactList = node1.next;
        }

        node1.prev = null;
        node1.next = null;

        // Remove from body 2
        if (node2.prev) {
          node2.prev.next = node2.next;
        }

        if (node2.next) {
          node2.next.prev = node2.prev;
        }

        if (node2 == body2.m_contactList) {
          body2.m_contactList = node2.next;
        }

        node2.prev = null;
        node2.next = null;
      }
    }
  },

  m_world: null,

  // This lets us provide broadphase proxy pair user data for
  // contacts that shouldn't exist.
  m_nullContact: new $.Engine.Dynamic.Contact.NullContact(),
  m_destroyImmediate: null
});